#pragma once
#ifndef _LPE_RESMAN_RESHASHMAP_H_
#define _LPE_RESMAN_RESHASHMAP_H_

#include "core/core_utils.h"
#include <boost/unordered_map.hpp>
#include "Log/LogSystem.h"

namespace lpe{

	namespace core{
		class CUniqueIDXGenerator;
	}

	namespace resman{

		class Resource;

		enum ReleaseResourePolicy
		{
			ON_ZERO_COUNT,
			NEVER,
			ON_DEMAND,
			ON_ITEM_COUNT
			/*,ON_MEMORY_LIMIT*/
		};

		class ResourceHashMap
		{
		public:
			ResourceHashMap()
				:m_itemCount(0)
				,m_relPolicy(ON_ZERO_COUNT)
				,m_policyTreshold(0)
				,DEF_APPENDER("[RESHASHMAP]")
			{}
			ResourceHashMap(const std::string& appender)
				:m_itemCount(0)
				,m_relPolicy(ON_ZERO_COUNT)
				,m_policyTreshold(0)
				,DEF_APPENDER(appender)
			{}

			/**
			 * Carga un recurso desde un fichero de metadatos. Devuelve el ID que genera.
			 */
			const uint32_t loadResourceFromMetadataFile(const std::string& fileName, Resource* res);
			/**
			 * Un simple alias para findResourceById
			 */
			Resource* acquireResource(const uint32_t id);
			void releaseResource(const uint32_t id);
			void releaseResource(const Resource* res);
			const uint32_t cloneResource(const Resource* start, Resource* end);

			/**
			 * Comprueba si el recurso ya esta cargado, y deveulve verdadero si ya esta cargado y falso en cualquier otro caso.
			 *
			 * @param id El id que buscamos
			 * @return true si se ha encontrado el recurso.
			 */
			bool isResourceLoaded(const uint32_t id);
			/**
			 * Define la politica de liberacion de recursos del motor. Una idea que hay que tener claro es que cargar un recurso tiene un coste.
			 * Si lo dejamos en memoria, gasta memoria. Si lo liberamos cuando el refcount llega a 0, entonces podemos tener que cargarlo varias veces. Debemos
			 * decidir que tipo de politica tener para nuestro sistema de recursos. Decidimos la politica llamando a esta funcion con un item de tipo ReleaseResourePolicy.
			 *
			 * Las politicas son:
			 * ON_ZERO_COUNT: Cada vez que el refcount llega a 0 se libera el recurso. El parametro treshold se ignora.
			 * NEVER: No se libera. El recurso se queda cargado durante la ejecución del programa. El parámetro treshold se ignora.
			 * ON_DEMAND: Cada vez que se llama al método releaseUnusedResources, se liberan aquellos recursos con refCount = 0. El parámetro treshold se ignora.
			 * ON_ITEM_COUNT: Cada vez que se llama el método releaseUnusedResources, se liberan los recursos hasta que el numero de recursos cargados esta por debajo del treshold.
			 */
			void defineReleaseResourcePolicy(ReleaseResourePolicy policyItem, const uint32_t treshold );
		private:
			class ResourceWithRefCounter
			{
			private:
				Resource* m_resource;
				uint32_t  m_refCount;
				DISALLOW_COPY_AND_ASSIGN(ResourceWithRefCounter);
			public:
				ResourceWithRefCounter(Resource* res);
				~ResourceWithRefCounter();
				const uint32_t incCount();
				const uint32_t decrCount();
				bool releaseOnZeroCount();
				Resource* getResource() {
					incCount();
					return m_resource;
				}
				const Resource* getResource() const{return m_resource;}
			};
			/// inner counter to quantify alocated objects
			uint32_t m_itemCount;
			ReleaseResourePolicy m_relPolicy;

			uint32_t m_policyTreshold;
			/// Default appender to logging purposes
			const std::string DEF_APPENDER;
			/// The log object
			glog::CGlobalLog* m_log;

			/// hashMap donde guardo los recursos cambiados
			boost::unordered_map<const uint32_t, ResourceWithRefCounter*> m_resourceMap;
			/// generador de identificadores unicos
			lpe::core::CUniqueIDXGenerator* m_idGenerator;

			void init();
		};
	}
}

#endif //_LPE_RESMAN_RESHASHMAP_H_
